using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Text;
using System.Windows.Forms;
using Av.Utils;
using Solo.Core;
using System.Collections;
using System.IO;
using System.Reflection;
using System.Threading;

namespace SiteStatusTracker
{
    internal partial class Form2 : Form
    {
        private GetStatusThread m_statusThread = null;

        protected NotifyIcon m_icon = null;

        /// <summary>
        /// How many errors queue will holds
        /// </summary>
        protected int m_maxErrors = 3;

        /// <summary>
        /// Contains up-to 10 last errors
        /// </summary>
        protected Queue m_errorQ = null;

        /// <summary>
        /// Stores and sends information
        /// </summary>
        protected Informer m_informer = new Informer();

        #region " Get/set text methods (invoked) "

        delegate void SetTextDelegate( string name, string value );
        delegate string GetTextDelegate( string name );

        /// <summary>
        /// Returns text of control.
        /// </summary>
        /// <param name="name">Name of control</param>
        /// <returns>Text of control. Returns "checked"/"" or "" for CheckBox</returns>
        private string GetText( string name )
        {
            string s = "";

            try
            {
                if( this.InvokeRequired )
                {
                    this.Invoke(new GetTextDelegate(GetText), name);
                } else
                {
                    Control[] ar = this.Controls.Find(name, false);
                    if( (ar != null) && (ar.Length > 0) )
                    {
                        if( ar[0].GetType() == typeof(CheckBox) )
                        {
                            s = ((CheckBox)ar[0]).Checked ? "checked" : "";
                        } else
                        {
                            s = ar[0].Text;
                        }
                    }
                }
            } catch( Exception ex )
            {
            }

            return s;
        }

        /// <summary>
        /// Thread safe AddText
        /// </summary>
        /// <param name="name">Name of control to add text</param>
        /// <param name="value">Text to add</param>
        private void AddText( string name, string value )
        {
            try
            {
                if( this.InvokeRequired )
                {
                    this.Invoke(new SetTextDelegate(AddText), name, value);
                } else
                {
                    Control[] ar = this.Controls.Find(name, false);
                    if( (ar != null) && (ar.Length > 0) )
                    {
                        ar[0].Text += value;
                        if( ar[0].GetType() == typeof(TextBox) )
                        {
                            ((TextBox)ar[0]).SelectionStart = Int32.MaxValue;
                        }
                    }
                }
            } catch( Exception )
            {
            }
        }

        /// <summary>
        /// Thread safe SetText
        /// </summary>
        /// <param name="name">Name of control to set text</param>
        /// <param name="value">Text to set</param>
        private void SetText(string name, string value)
        {
            try
            {
                if( this.InvokeRequired )
                {
                    this.Invoke(new SetTextDelegate(SetText), name, value);
                } else
                {
                    Control[] ar = this.Controls.Find(name, false);
                    if( (ar != null) && (ar.Length > 0) )
                    {
                        ar[0].Text = value;
                    }
                }

            } catch( Exception ex )
            {
            }
        }

        #endregion

        public Form2()
        {
            Log4cs.dir = Misc.GetPath() + "Logs\\";
            Log4cs.outputToConsole = false;
            Log4cs.logName = "sitetrack_{0}.log";

            this.Visible = false;
            this.ShowInTaskbar = false;
            this.Opacity = 0;
        }

        protected override void OnLoad( EventArgs e )
        {
            try
            {
                Log4cs.Log("Starting {0} (v{1})...", Settings.Name, Settings.Version);
                InitializeComponent();

                bool createdNew = false;
                Mutex mx = new Mutex(false, "SiteStatusTracker", out createdNew);
                Log4cs.Log(Importance.Debug, "Is mutex created: {0}", createdNew);
                
                // If application is already running
                if(createdNew == false)
                {
                    throw new ApplicationException("Application is already running!");
                }


                this.Text = string.Format("{0} (v{1})", Settings.Name, Settings.Version);

                Settings.Load();
                Log4cs.Log(Settings.ToString());

                //m_informer.Add(Settings.TrackerItems[0]);
                //m_informer.ForceInform();


                Log4cs.Log("Initializing error queue for {0} errors", m_maxErrors);
                m_errorQ = new Queue(m_maxErrors);

                // Create tray icon
                Assembly asm = Assembly.GetExecutingAssembly();
                FileInfo fi = new FileInfo(asm.GetName().Name);
                using( Stream s = asm.GetManifestResourceStream(string.Format("{0}.av1.ico", fi.Name)) )
                {
                    m_icon = new NotifyIcon();
                    m_icon.Visible = true;
                    m_icon.Icon = new Icon(s);
                }

                // Create context menu for tray icon
                MenuItem[] arMenu = this.CreateMenuItems();
                if( arMenu != null )
                    m_icon.ContextMenu = new ContextMenu(arMenu);

                m_statusThread = new GetStatusThread();
                
                //throw new Exception("Test");
                
                // Assign callback for OK and failed actions
                m_statusThread.SiteTrackedOk += new SiteIsTrackedDelegate(OnSiteTrackedOk);
                m_statusThread.SiteTrackedFail += new SiteIsTrackedDelegate(OnSiteTrackedFail);
                m_statusThread.AllSitesDone += new SiteWholeListDoneDelegate(OnAllSitesChecked);

            } catch( Exception ex )
            {
                Log4cs.Log(Importance.Error, "Error loading main form!");
                Log4cs.Log(Importance.Debug, ex.ToString());
                MessageBox.Show("Error loading application!", string.Format("{0} (v{1})", Settings.Name, Settings.Version), MessageBoxButtons.OK, MessageBoxIcon.Error);
                this.Close();
            }
        }

        void OnAllSitesChecked(TrackerItem[] arSite)
        {
            m_informer.Inform();
        }

        /// <summary>
        /// Creates context menu for tray icon
        /// </summary>
        /// <returns></returns>
        private MenuItem[] CreateMenuItems()
        {
            MenuItem[] arMenu = null;

            try
            {
                // Got quantity of menus
                arMenu = new MenuItem[MyMenu.Size];

                for( int i = 0; i < MyMenu.Size; i++ )
                {
                    arMenu[i] = new MenuItem(MyMenu.ToName(i), OnContextMenuClicked);
                }

                // By default status thread is stopped, so disable "Stop" command
                arMenu[MyMenu.Position.Stop].Enabled = false;

                // Format "Version"
                arMenu[MyMenu.Position.Version].Text = string.Format("{0} (v{1})", Settings.Name, Settings.Version);

            } catch( Exception ex )
            {
                Log4cs.Log(Importance.Error, "Error creating menu items!");
                Log4cs.Log(Importance.Debug, ex.ToString());
            }
            
            return arMenu;
        }

        /// <summary>
        /// Performs actions in case status was failed
        /// </summary>
        /// <param name="item">Item which check failed</param>
        void OnSiteTrackedFail( TrackerItem item )
        {
            // TODO: Thread safe!

            //Log4cs.Log(Importance.Warning, "Got error for site: {0}...", item.SiteUri);
            //ErrorItem err = new ErrorItem();
            //err.TaskName = item.Name;
            //err.Site = item.SiteUri.ToString();
            //err.ErrorTime = DateTime.Now;

            //// Saving this error in log file
            //this.BackupError(err);

            //if( m_errorQ.Count == m_maxErrors )
            //{
            //    Log4cs.Log(Importance.Debug, "Error queue reaches its limit, dequeue one...");
            //    m_errorQ.Dequeue();
            //}

            //m_errorQ.Enqueue(err);
            if( item.PopupFailS > 0 )
            {
                m_icon.ShowBalloonTip(item.PopupFailS * 1000, Settings.Name, item.SiteUri + " - FAILED", ToolTipIcon.Error); 
            }
        }

        /// <summary>
        /// Saves error log to file
        /// </summary>
        /// <param name="old">Item to be saved</param>
        private void BackupError( ErrorItem old )
        {
            try
            {
                string backupDir = Misc.GetPath() + "Status\\" + old.TaskName + "\\";
                string filename = string.Format("{0}.log", old.ErrorTime.ToString("yyyyMMdd"));
                Log4cs.Log("Backuping error in: {0}{1}", backupDir, filename);

                DirectoryInfo di = new DirectoryInfo(backupDir);
                if( !di.Exists )
                {
                    di.Create();
                }

                File.AppendAllText(backupDir + filename, string.Format("{0} {1} FAILED{2}", old.ErrorTime.ToString("HH:mm:ss"), old.TaskName, Environment.NewLine));
            } catch( Exception ex )
            {
                Log4cs.Log(Importance.Error, "Error saving error!");
                Log4cs.Log(Importance.Debug, ex.ToString());
            }
        }

        /// <summary>
        /// Performs actions in case status was OK
        /// </summary>
        /// <param name="item">Item which check was OK</param>
        void OnSiteTrackedOk(TrackerItem item)
        {
            Log4cs.Log("FOUND for site: {0}", item.SiteUri);
            if( item.PopupOkS > 0 )
            {
                m_icon.ShowBalloonTip(item.PopupOkS * 1000, Settings.Name, item.SiteUri + " - OK", ToolTipIcon.Info);
            }

            m_informer.Add(item);
        }

        void OnContextMenuClicked( object sender, EventArgs e )
        {
            try
            {
                switch( ((MenuItem)sender).Index )
                {
                    case MyMenu.Position.Start:
                        m_icon.ContextMenu.MenuItems[0].Enabled = false;
                        m_icon.ContextMenu.MenuItems[1].Enabled = true;
                        OnStartStopClicked(null, null);
                        break;
                    case MyMenu.Position.Stop:
                        m_icon.ContextMenu.MenuItems[0].Enabled = true;
                        m_icon.ContextMenu.MenuItems[1].Enabled = false;
                        OnStartStopClicked(null, null);
                        break;
                    case MyMenu.Position.Version:
                        MessageBox.Show("Site Status Tracker by mr. Aleksej Vasinov", Settings.NameVersion, MessageBoxButtons.OK, MessageBoxIcon.Information);
                        break;
                    case MyMenu.Position.Reload:
                        ReloadSettings();
                        break;
                    case MyMenu.Position.LastErrors:
                        if( m_errorQ.Count > 0 )
                        {
                            Log4cs.Log("Showing last errors...");
                            object[] obj = m_errorQ.ToArray();
                            StringBuilder sb = new StringBuilder();
                            foreach( ErrorItem err in obj )
                            {
                                sb.AppendFormat("{0} {1}{2}", err.ErrorTime.ToString("HH:mm:ss"), err.TaskName, Environment.NewLine);
                            }
                            MessageBox.Show(sb.ToString(), Settings.Name, MessageBoxButtons.OK, MessageBoxIcon.Information);

                        } else
                            Log4cs.Log(Importance.Warning, "No errors to show!");
                        break;
                    default:
                        this.Close();
                        break;
                }

            } catch( Exception ex )
            {
                Log4cs.Log(Importance.Error, "Error handling context menu click!");
                Log4cs.Log(Importance.Debug, ex.ToString());
            }
        }

        /// <summary>
        /// Reload setting from XML file. Stops thread if necessary
        /// </summary>
        private void ReloadSettings()
        {
            Log4cs.Log("Reloading settings...");
            if( m_statusThread.Working )
            {
                m_statusThread.Stop();
            }
            Settings.Load();
        }

        /// <summary>
        /// Starts or stops status check thread
        /// </summary>
        private void OnStartStopClicked( object sender, EventArgs e )
        {
            Log4cs.Log("Start/stop thread is called...");
            if( m_statusThread.Working )
            {
                m_statusThread.Stop();
            } else
            {
                m_statusThread.Start();
            }
        }

        private void OnFormClosing( object sender, FormClosingEventArgs e )
        {
            Log4cs.Log("Main form is closing...");
            if( m_icon != null )
                m_icon.Dispose();
            
            if( m_statusThread != null )
            {
                m_statusThread.Stop();
            }
        }

        protected override CreateParams CreateParams
        {
            get
            {
                CreateParams cp = base.CreateParams;
                // Turns on WS_EX_TOOLWINDOW style bit to hide from Alt-TAB list
                cp.ExStyle |= 0x80;
                return cp;
            }
        }

    }

}